﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Input;
using System.Collections.ObjectModel;
using System.Windows;
using System.Linq.Expressions;

using Caliburn.Micro;

using Ingenious.Core;

namespace Ingenious.MVVM
{
    public abstract class ViewModelBase<TModel> : PropertyChangedBase, IViewModel
        where TModel : IModel
    {
        private IViewModel m_parent;
        private TModel m_model;

        #region Object Lifetime
        public ViewModelBase( IViewModel parent, TModel model )
        {
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }

            // Note: Allow a null parent, as this may be a top level view model item
            m_parent = parent;
            m_model = model;

            // SPONG: This should be a weak event!
            m_model.PropertyChanged += new PropertyChangedEventHandler(OnModelPropertyChanged);
        }

        public ViewModelBase()
        {
        }
        #endregion

        #region Model Property Changed
        protected virtual void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // Default do nothing

            return;
        }
        #endregion

        #region Command Bindings
        public virtual void OnAddCommandBindings(ICollection<CommandBinding> commandBindings)
        {
            return;
        }
        #endregion

        protected T GetParentOfType<T>()
            where T : IViewModel
        {
            T match = default(T);

            IViewModel vm = this;

            while (vm.Parent != null)
            {
                if (vm.Parent is T)
                {
                    return (T)vm.Parent;
                }

                vm = vm.Parent;
            }

            return match;
        }

        #region Properties

        public TModel TypedModel
        {
            get { return m_model; }
        }

        public IModel Model
        {
            get { return (IModel)m_model; }
        }

        public IViewModel Parent
        {
            get { return m_parent; }
            set
            {
                m_parent = value;
                NotifyOfPropertyChange(() => Parent);
            }
        }

        #endregion
    }
}
